﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;

using Peradigm.Framework.Domain.Parties;
using Peradigm.Framework.Domain.Security;
using Peradigm.Framework.Persistence;
using Peradigm.Framework.Security;
using Peradigm.Framework.Persistence.Exceptions;

using Liberty.Domain.ApplicationManagement;
using Liberty.Domain.ApprovalManagement;
using Liberty.Domain.GrantManagement;
using Liberty.Domain.Kernel;

using Liberty.Persistence.Kernel;

namespace Liberty.Persistence.ApprovalManagement
{

    [Serializable]
    public class EventBroker : AbstractBroker
    {
        #region Constants
        private const string
            SP_FOLLOW_UP_EVENTS_FOR_USER = "[ApprovalManagement].[FollowUpEventsForUser]",
            SP_EVENTS_FOR_GRANT = "GrantManagement.EventsForGrant",
            SP_SAVE_GRANT_EVENT = "GrantManagement.SaveGrantEvent";

        private const string
            PARAM_USER_ID = "@userGuid";

        private const string FIELD_PREFIX = "@";

        private const string
            FLD_OID = "OID",
            FLD_VERSION = "Version",
            FLD_ACTIVITY_ID = "ActivityID",
            FLD_EVENT_NOTES = "EventNotes",
            FLD_CREATED_BY = "CreatedBy",
            FLD_CREATED_ON = "CreatedOn",
            FLD_COMPLETED_BY = "CompletedBy",
            FLD_COMPLETED_ON = "CompletedOn";

        private const string FLD_GRANT_ID = "GrantID";

        private const string ERROR_SAVE_GRANT_EVENT = "Unable to save grant event.";
        #endregion

        #region Event Handlers
        #endregion

        #region Methods
        public List<FollowUpEvent> FollowUpEventsFor( User user )
        {
            List<FollowUpEvent> events = new List<FollowUpEvent>();
            SqlCommand cmd = null;
            SafeReader reader = null;
            
            try
            {
                cmd = this.CommandFor( SP_FOLLOW_UP_EVENTS_FOR_USER );
                cmd.Parameters.AddWithValue( PARAM_USER_ID, user.Guid );
                reader = this.ExecuteReader( cmd );
                try
                {
                    while( reader.Read() )
                    {
                        events.Add( this.FollowUpEventFrom( reader ) );
                    }
                }
                finally
                {
                    this.Close( reader );
                }
            }
            finally
            {
                this.Close( cmd );
            }
            return events;
        }

        public void EventsFor( Grant grant )
        {
            SqlCommand cmd = null;
            SafeReader reader = null;

            try
            {
                cmd = this.CommandFor( SP_EVENTS_FOR_GRANT );

                cmd.Parameters.Add( new SqlParameter( FIELD_PREFIX + FLD_GRANT_ID, grant.Oid ) );

                // Execute and read oid/timestamp
                reader = this.ExecuteReader( cmd );
                while( reader.Read() )
                {
                    Event myEvent = EventFrom( reader );
                    grant.AddEvent( myEvent );
                }
            }
            finally
            {
                this.Close( reader );
                this.Close( cmd );
            }
        }

        public void SaveEvents( Grant grant )
        {
            SqlCommand cmd = null;
            SafeReader reader = null;

            ActivityBroker activityBroker = new ActivityBroker( this.ConnectionString );

            foreach( Event grantEvent in grant.Events )
            {
                try
                {
                    cmd = this.CommandFor( SP_SAVE_GRANT_EVENT );

                    cmd.Parameters.Add( new SqlParameter( FIELD_PREFIX + FLD_OID, grantEvent.Oid ) );
                    cmd.Parameters.Add( new SqlParameter( FIELD_PREFIX + FLD_VERSION, grantEvent.Version ) );
                    cmd.Parameters.Add( new SqlParameter( FIELD_PREFIX + FLD_GRANT_ID, grant.Oid ) );

                    // If the activity doesn't have a unique identifier, try doing a lookup based on the description.
                    if( grantEvent.Activity.Oid == PersistentModel.NEW_OID )
                    {
#warning This will cause a null pointer exception if the activity is not found.
                        grantEvent.Activity = activityBroker.ActivityWith( grantEvent.Activity.Description );
                    }
                    cmd.Parameters.Add( new SqlParameter( FIELD_PREFIX + FLD_ACTIVITY_ID, grantEvent.Activity.Oid ) );
                    cmd.Parameters.Add( new SqlParameter( FIELD_PREFIX + FLD_EVENT_NOTES, grantEvent.Note ) );
                    cmd.Parameters.AddWithValue( FIELD_PREFIX + FLD_CREATED_BY, grantEvent.CreatedBy.Guid );
                    cmd.Parameters.Add( new SqlParameter( FIELD_PREFIX + FLD_CREATED_ON, grantEvent.OccuredOn ) );
                    if( grantEvent.CompletedBy != null )
                    {
                        cmd.Parameters.AddWithValue( FIELD_PREFIX + FLD_COMPLETED_BY, grantEvent.CompletedBy.Guid );
                        cmd.Parameters.Add( new SqlParameter( FIELD_PREFIX + FLD_COMPLETED_ON, grantEvent.CompletedOn ) );
                    }

                    // Execute and read oid/timestamp
                    reader = this.ExecuteReader( cmd );
                    if( reader.Read() )
                    {
                        grantEvent.Oid = reader.GetInt64( FLD_OID );
                        grantEvent.Version = reader.GetRowVersion( FLD_VERSION );
                    }
                    else
                    {
                        throw new ObjectNotSavedException( ERROR_SAVE_GRANT_EVENT );
                    }
                }
                finally
                {
                    this.Close( reader );
                    this.Close( cmd );
                }
            }
        }
        #endregion

        #region Properties
        #endregion

        #region Private Methods
        private FollowUpEvent FollowUpEventFrom( SafeReader reader )
        {
            long oid = reader.GetInt64( FLD_OID );
            byte[] version = reader.GetRowVersion( FLD_VERSION );
            Activity activity = new ActivityBroker( this.ConnectionString ).ActivityWith( reader.GetInt64( FLD_ACTIVITY_ID ) );
            string notes = reader.GetString( FLD_EVENT_NOTES );
            DateTime createdOn = reader.GetDateTime( FLD_CREATED_ON );
            Guid createdByGuid = reader.GetGuid( FLD_CREATED_BY );
            SecurityService securityService = new SecurityService();
            User createdBy = securityService.UserWith( createdByGuid );
            DateTime completedOn = reader.GetDateTime( FLD_COMPLETED_ON );
            User completedBy = null;
            if ( !reader.IsDBNull( FLD_COMPLETED_BY ) )
            {
                Guid completedByGuid = reader.GetGuid( FLD_COMPLETED_BY );
                if ( completedByGuid != null )
                {
                    completedBy = securityService.UserWith( completedByGuid );
                }
            }
            PersonBroker personBroker = new PersonBroker( this.ConnectionString );
            Person recipient = personBroker.PersonFrom( reader );
            personBroker.AddContactPointsFrom( recipient, reader );

            FollowUpEvent followupEvent = new FollowUpEvent( oid, version, activity, createdOn, createdBy, recipient );
            followupEvent.CompletedBy = completedBy;
            followupEvent.CompletedOn = completedOn;
            followupEvent.Note = notes;
            return followupEvent;
        }

#warning There is some overlap between EventFrom and FollowUpEventFrom that could be optimized.
        private Event EventFrom( SafeReader reader )
        {
            ActivityBroker activityBroker = new ActivityBroker( this.ConnectionString );
            SecurityService securityService = new SecurityService();

            Activity activity = activityBroker.ActivityWith( reader.GetInt64( FLD_ACTIVITY_ID ) );
            Guid createdByGuid = reader.GetGuid( FLD_CREATED_BY );
            User createdBy = securityService.UserWith( createdByGuid );
            Event myEvent = new Event(
                                reader.GetInt64( FLD_OID ),
                                reader.GetRowVersion( FLD_VERSION ),
                                activity,
                                reader.GetDateTime( FLD_CREATED_ON ),
                                createdBy );
            myEvent.Note = reader.GetString( FLD_EVENT_NOTES );
            myEvent.CompletedOn = reader.GetDateTime( FLD_COMPLETED_ON );
            User completedBy = null;
            if( !reader.IsDBNull( FLD_COMPLETED_BY ) )
            {
                Guid completedByGuid = reader.GetGuid( FLD_COMPLETED_BY );
                if( completedByGuid != null )
                {
                    completedBy = securityService.UserWith( completedByGuid );
                }
            }
            myEvent.CompletedBy = completedBy;

            return myEvent;
        }
        #endregion

        #region Private Properties
        #endregion

        #region Construction and Finalization
        public EventBroker( SqlTransaction txn )
            : base( txn )
        {
            i_PersonBroker = new PersonBroker( txn );
        }
        public EventBroker( string cxnString )
            : base( cxnString )
        {
            i_PersonBroker = new PersonBroker( cxnString );
        }
        #endregion

        #region Data Elements
        PersonBroker i_PersonBroker = null;
        #endregion
    }
}
